%%%-------------------------------------------------------------------
%%% @author liuwentao
%%% @doc
%%%       时间处理系统
%%% @end
%%% Created : 24. 5月 2021 16:56
%%%-------------------------------------------------------------------
-module(date).
-author("liuwentao").

%% API
-export([
    unixtime/0
    , unixtime/1
    , week_num/0
    , week_num/1
    , unixtime_is_same_day/2
    , unixtime_diff/1
    , unixtime_diff/2
    , datetime_to_seconds/1
    , seconds_to_datetime/1
]).

-define(week_sec, 604800).
-define(day_sec, 86400).
-define(hour_sec, 3600).
-define(minu_sec, 60).



%% @doc 获取时间撮
unixtime() ->
    erlang:system_time(second).

%% @doc 获得当前时间戳
unixtime(now) ->
    {M, S, _} = erlang:timestamp(),
    M * 1000000 + S;
unixtime(ms) ->
    {S1, S2, S3} = erlang:timestamp(),
    trunc(S1 * 1000000000 + S2 * 1000 + S3 / 1000);
unixtime(today) ->
    {M, S, MS} = erlang:timestamp(),
    {_Data, Time} = calendar:now_to_local_time({M, S, MS}),
    M * 1000000 + S - calendar:time_to_seconds(Time);
unixtime(tomorrow) ->
    unixtime(today) + ?day_sec;
unixtime(monday) ->
    unixtime(today) - ?day_sec * (week_num() - 1);
unixtime(next_monday) ->
    unixtime(today) + ?day_sec * (8 - week_num());
unixtime(today_five) ->
    unixtime({next, {5, 0, 0}}) - ?day_sec;
%% 今天0:0:0时间戳 + Hour
%% Hour = int() 今天0:0:0的时间戳
%% return int()
unixtime({hour, Hour}) when is_integer(Hour) ->
    unixtime(today) + Hour * ?hour_sec;
%% 获取某时间戳的00:00:00的时间戳当Unixtime为0时，返回值有可能是负值，因为这里有时区偏移值（例如北京时间就可能是-28800
unixtime({today, Unixtime}) ->
    Base = unixtime(today),
    case Unixtime > Base of
        false -> Base - erlang:ceil((Base - Unixtime) / ?day_sec) * ?day_sec;
        true -> (Unixtime - Base) div ?day_sec * ?day_sec + Base
    end;
unixtime({today_five, Unixtime}) ->
    Base = unixtime(today_five),
    case Unixtime > Base of
        false -> Base - erlang:ceil((Base - Unixtime) / ?day_sec) * ?day_sec;
        true -> (Unixtime - Base) div ?day_sec * ?day_sec + Base
    end;
unixtime({H, M, S}) ->
    unixtime(today) + ?hour_sec * H + ?minu_sec * M + S;
unixtime({next, {H, M, S}}) ->
    Ts = unixtime({H, M, S}),
    case unixtime() >= Ts of
        true -> Ts + ?day_sec;
        false -> Ts
    end.


%% @doc 获取今天是星期几1-7
week_num() ->
    {Date, _} = calendar:local_time(),
    calendar:day_of_the_week(Date).
week_num(Unixtime) ->
    {Date, _} = from_unixtime(Unixtime),
    calendar:day_of_the_week(Date).

%% @doc 时间戳转换成日期
from_unixtime(Unixtime) ->
    DT = calendar:gregorian_seconds_to_datetime(Unixtime + calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}})),
    erlang:universaltime_to_localtime(DT).

%% @doc 判断是否是同一天
unixtime_is_same_day(Timestamp1, Timestamp2) ->
    {{Y1, M1, D1}, _} = from_unixtime(Timestamp1),
    case from_unixtime(Timestamp2) of
        {{Y1, M1, D1}, _} ->
            true;
        _ ->
            false
    end.

%% @doc 取时间差
unixtime_diff(UT2) ->
    unixtime_diff(UT2, now).

%% @doc UT2和UT1的相差秒数 UT2 - UT1
unixtime_diff(UT2, UT1) ->
    TS1 = case is_integer(UT1) of true -> UT1; false -> unixtime(UT1) end,
    TS2 = case is_integer(UT2) of true -> UT2; false -> unixtime(UT2) end,
    TS2 - TS1.



%% @doc 将日期转换unix时间戳
-spec datetime_to_seconds(DateTime) -> false | Seconds when
    DateTime :: {{Y, M, D}, {H, M, S}},
    Y :: pos_integer(),
    M :: 1..12,
    D :: 1..31,
    H :: 0..23,
    M :: 0..59,
    S :: 0..59,
    Seconds :: pos_integer().
datetime_to_seconds({Year, Month, Day, Hour, Minute, Second}) ->
    datetime_to_seconds({{Year, Month, Day}, {Hour, Minute, Second}});
datetime_to_seconds(DateTime) ->
    case get({datetime_to_seconds, DateTime}) of %% 增加缓存 时间计算很消耗CPU
        T when is_integer(T) -> T;
        _ ->
            case calendar:local_time_to_universal_time_dst(DateTime) of
                [] -> false;
                [_, Udate] ->
                    T = calendar:datetime_to_gregorian_seconds(Udate) - 719528 * 24 * 3600,
                    put({datetime_to_seconds, DateTime}, T),
                    T;
                [Udate] ->
                    T = calendar:datetime_to_gregorian_seconds(Udate) - 719528 * 24 * 3600,
                    put({datetime_to_seconds, DateTime}, T),
                    T
            end
    end.

%% @doc 将Unixtime转换成当地时间
-spec seconds_to_datetime(Unixtime) -> {{Y :: non_neg_integer(), M :: non_neg_integer(), D:: non_neg_integer()}, {HH :: non_neg_integer(), MM :: non_neg_integer, SS :: non_neg_integer()}} when
    Unixtime :: non_neg_integer().
seconds_to_datetime(Unixtime) ->
    DT = calendar:gregorian_seconds_to_datetime(Unixtime + calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}})),
    erlang:universaltime_to_localtime(DT).